package com.github.llyb120.qiuya.storage;

import com.github.llyb120.qiuya.type.QString;
import com.github.llyb120.qiuya.util.Convert;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.LinkedList;

import static com.github.llyb120.qiuya.util.Convert.byte2short;
import static com.github.llyb120.qiuya.util.Convert.short2byte;

public final class VirtualFile {
    private LinkedList<FileRect> rects = new LinkedList<>();
    private File dir;
    private String fileName;

//    private static final int[] sizes = {
//            16777216,
//            33554432,
//            67108864,
//            134217728,
//            268435456,
//            536870912,
//            1073741824
//    };

    VirtualFile(){}

    public VirtualFile(File file)  {
//        this.fake = file;
        this.fileName = file.getName();
        dir = file.getParentFile();
        if (!dir.exists()) {
            dir.mkdirs();
        }
        loadRects();
    }

    public boolean isNew(){
        return rects.isEmpty();
    }

    void loadRects()  {
        for (int i = 0; i < 100; i++) {
            File file = new File(dir, getRectName(i));
            if (file.exists()) {
                this.rects.addLast(new FileRect(file, getRectSize(i), i, getTotalRectSize(i)));
            } else {
                break;
            }
        }
    }

    FileRect addRect()  {
        FileRect result;
        int i = rects.size();
        File file = new File(dir, getRectName(i));
        this.rects.addLast(result = new FileRect(file, getRectSize(i), i, getTotalRectSize(i)));
        return result;
    }

    FileRect getRect(int i)  {
        if (i < rects.size()) {
            return rects.get(i);
        } else {
            for (int i1 = rects.size(); i1 < i + 1; i1++) {
                addRect();
            }
            return this.rects.getLast();
        }
    }

    /**
     * 64M 起 最大1.99G
     *
     * @param i
     * @return
     */
    int getRectSize(int i) {
        if(i < 0){
            return 0;
        }
        return i > 4 ? Integer.MAX_VALUE : 1 << (26 + i);//sizes[i];
    }

    String getRectName(int i) {
        return fileName + "_" + i;
    }

    long getTotalRectSize(int i){
        if(i < 0){
            return 0;
        }
        if(i > 4){
            return getTotalRectSize(4) + ((long)Integer.MAX_VALUE) * (i - 4);
        } else {
            return getRectSize(i) * 2 - getRectSize(0);//getRectSize(0) + getRectSize(i)) * (i+1) / 2;
        }
    }

    public FileRect seek(long offset)  {
        long total = 0;
        int i = 0;
        while (true) {
            total += getRectSize(i);
            if(total >= offset){
                break;
            }
            i++;
        }
        return getRect(i);//i
    }

    /**
     * 重设虚拟文件的大小，该方法会实际影响文件内容，删除不需要的文件
     *
     * @param size
     * @
     */
    public void resetSize(long size)  {
        FileRect rect = seek(size);
        for (int i = rects.indexOf(rect) + 1; i < rects.size(); i++) {
            rects.get(i).delete();
        }
    }

    public void clean(){
        for (FileRect rect : rects) {
            rect.delete();
        }
    }



    public VirtualFile writeBytes(long offset, byte[] bs)  {
        return writeBytes(offset, bs, 0, bs.length);
    }

    public VirtualFile writeBytes(long offset, byte[] bs, int start, int length)  {
        FileRect rect = seek(offset);
        //如果剩余空间不够
        int realOffset = (int) (offset - getTotalRectSize(rect.getIndex() - 1));//rect.getTotalSize() - offset);
        int realLen = Math.min(length, rect.getFileSize() - realOffset);
        rect.writeBytes(realOffset, bs, start, realLen);
        if(realLen < length){
            rect = getRect(rect.getIndex() + 1);
            rect.writeBytes(0, bs, realLen, length - realLen);
        }
        return this;
    }

    public byte[] readBytes(long offset, int length)  {
        return readBytes(offset, new byte[length]);
    }

    public byte[] readBytes(long offset, byte[] bs) {
        return readBytes(offset, bs, 0, bs.length);
    }

    public byte[] readBytes(long offset, byte[] bs, int start, int length){
        FileRect rect = seek(offset);
        //如果剩余空间不够
        int realOffset = (int) (offset - getTotalRectSize(rect.getIndex() - 1));//rect.getTotalSize() - offset);
        int realLen = Math.min(length, rect.getFileSize() - realOffset);
        rect.readBytes(realOffset, bs, start, realLen);
        if(realLen < length){
            rect = getRect(rect.getIndex() + 1);
            rect.readBytes(0, bs, realLen, length - realLen);
        }
        return bs;
    }

    public int readInt(long offset)  {
        byte[] bs = readBytes(offset, Integer.BYTES);
        return Convert.byte2int(bs);
    }

    public VirtualFile writeInt(long offset, int value)  {
        byte[] bs = Convert.int2byte(value);
        return writeBytes(offset, bs, 0, bs.length);
    }

    public String readString(long offset, int len){
        byte[] bs = readBytes(offset, len);
    return new String(bs, StandardCharsets.UTF_8);
    }

    public short readShort(long offset){
        byte[] bs = readBytes(offset, Short.BYTES);
        return byte2short(bs);
    }

    public VirtualFile writeShort(long offset, short value){
        byte[] bs = short2byte(value);
        return writeBytes(offset, bs);
    }

    public QString readQString(long offset){
        int len = readInt(offset);
        String str = readString(offset + Integer.BYTES, len);
        return new QString();
    }

    public long readLong(long offset){
        byte[] bs = readBytes(offset, Long.BYTES);
        return Convert.byte2long(bs);
    }


}
